package com.example.dayu521.sg_oss.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.dayu521.sg_oss.domain.OssIndex;
import com.example.dayu521.sg_oss.domain.OssIndexRecover;
import com.example.dayu521.sg_oss.service.Oss;
import com.example.dayu521.sg_oss.service.OssIndexRecoverService;
import com.example.dayu521.sg_oss.mapper.OssIndexRecoverMapper;
import com.example.dayu521.sg_oss.service.OssIndexService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.dayu521.oss.Mq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import java.util.Map;

/**
* @author hasee
* @description 针对表【t_oss_index_recover】的数据库操作Service实现
* @createDate 2023-06-23 12:40:47
*/
@Service
@Slf4j
public class OssIndexRecoverServiceImpl extends ServiceImpl<OssIndexRecoverMapper, OssIndexRecover>
    implements OssIndexRecoverService{

    @Resource
    private OssIndexService ossIndexService;

    @Autowired
    private Oss oss;

    //不放事务的话,失败的恢复无法回滚
    @Transactional
    public boolean within_transaction_recover(Long node_id, Mq.Recover m) {
        var msg=m.obj_name();
        //DuplicateKeyException
        //这两个更新语句不被包含在事务中,从而不会导致正常流程阻塞

        //1 首先处理不必要的恢复.这个步骤主要针对于连续插入相同数据时产生的过早恢复,而实际第一个过程只是还未完成.这里再次检查下是否完成.不是必须的
        if(ossIndexService.count(new QueryWrapper<OssIndex>()
                .eq("obj_name", msg)
                .eq("status",1))==1l){
            log.error("不需要恢复");
            return false;
        }
        //2 禁止正常流程进行竞争.
        var msg_oss=ossIndexService.update(new UpdateWrapper<OssIndex>()
                .set("status",2)
                .eq("obj_name", msg));
        if(!msg_oss){
            log.error("""
                            1 数据库不存在此对象,终止恢复:{}""",msg);
            return false;
        }

        //上一步成功后,此时除了可以确认存在错误情况.还有有三种情况,正常过程正在处理中或者处理正常完成或者其他节点已经处于恢复过程

        var recover = new OssIndexRecover();
        recover.setNode_id(Math.toIntExact(node_id));
        recover.setDate(new Date());
        recover.setObj_name(msg);
        recover.setObj_id(m.obj_id());
        boolean is_save=false;
        try {
            //3 禁止并发恢复进行竞争
            //这里数据库会加锁
            is_save=save(recover);
        }catch (DuplicateKeyException e){
            log.error("检测与其他节点存在并发恢复,当前节点退出");
            return false;
        }
        if(!is_save){
            log.error("为什么保存错误?");
        }
        //此时剩两种情况:正常过程正在处理中或者处理正常完成
        //如果正在处理中,那么最终它会再次失败,触发当前恢复过程.第二次的恢复过程不会存在与正常流程进行竞争状态
        //如果处理正常完成,那么客户可能会看到返回成功,但数据还是被当前恢复删除了.
        // 但这表明恢复过程至少是由客户进行发起的.过早发起恢复,可能是客户等得不耐烦了,但终究是客户的原因,不能认为这是一个数据不一致错误
        if(!oss.rm_obj(msg)) {
            log.error("删除文件失败,不存在此文件:{}", msg);
        }
        if(!ossIndexService.removeByMap(Map.of("obj_name",msg,"status",2))){
            log.error("无法删除,没有此obj记录:{}",msg);
        }

        return true;
    }

}




